

#include "pipe_elements.h"
#include <iostream>
#include <stack>
using namespace std;
extern int debug;
extern stack <int> rsremovelist;
extern int flushed;
extern bits flushedby;
int  RS::full()
{
    for(int i=0;i<8;i++)
    {
        if(!station[i].busy)
            return i;

    }
    return -1;
}
void RS::setnotbusy(int a)
{
    station[a].busy=0;
    station[a].executed=0;
}
int RS::issue(bits instruction,int cycle,int freerob,int r,ROB &rROB,registerFile &rRF)
{
    station[r].cycle=cycle;
    station[r].busy=1;
    station[r].inst=instruction;
    station[r].robid=freerob;
    station[r].op=instruction.opcode;
    if(station[r].opADDtype())
    {
        if(rRF.isbusy(instruction.op1))
        {
            if(rROB.isready(rRF.getROB(instruction.op1)))
            {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op1)); station[r].qj=-1;}
            else
            station[r].qj=rRF.getROB(instruction.op1);

        }
        else
        {station[r].vj=rRF.getvalue(instruction.op1);station[r].qj=-1;}

       if(rRF.isbusy(instruction.op2))
        {
            if(rROB.isready(rRF.getROB(instruction.op2)))
            {station[r].vk=rROB.getvalue(rRF.getROB(instruction.op2));station[r].qk=-1;}
            else
            station[r].qk=rRF.getROB(instruction.op2);

        }
        else
        {station[r].vk=rRF.getvalue(instruction.op2);station[r].qk=-1;}


    }
    if(station[r].opADDItype())
    {
        if(rRF.isbusy(instruction.op1))
        {
            if(rROB.isready(rRF.getROB(instruction.op1)))
            {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op1)); station[r].qj=-1;}
            else
            station[r].qj=rRF.getROB(instruction.op1);

        }
        else
        {station[r].vj=rRF.getvalue(instruction.op1);station[r].qj=-1;}
        station[r].vk=instruction.immediate;
        station[r].qk=-1;

    }
    if(station[r].opSLLtype())
    {
                    if(rRF.isbusy(instruction.op1))
                    {
                        if(rROB.isready(rRF.getROB(instruction.op1)))
                        {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op1)); station[r].qj=-1;}
                        else
                        station[r].qj=rRF.getROB(instruction.op1);

                    }
                    else
                    {station[r].vj=rRF.getvalue(instruction.op1);station[r].qj=-1;}
                    station[r].vk=instruction.immediate;
                    station[r].qk=-1;

    }
    if(station[r].opBNEtype())
    {
                        if(rRF.isbusy(instruction.op1))
                        {
                            if(rROB.isready(rRF.getROB(instruction.op1)))
                            {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op1)); station[r].qj=-1;}
                            else
                            station[r].qj=rRF.getROB(instruction.op1);

                        }
                        else
                        {station[r].vj=rRF.getvalue(instruction.op1);station[r].qj=-1;}

                       if(rRF.isbusy(instruction.op2))
                        {
                            if(rROB.isready(rRF.getROB(instruction.op2)))
                            {station[r].vk=rROB.getvalue(rRF.getROB(instruction.op2));station[r].qk=-1;}
                            else
                            station[r].qk=rRF.getROB(instruction.op2);

                        }
                        else
                        {station[r].vk=rRF.getvalue(instruction.op2);station[r].qk=-1;}
    }
    if(station[r].opBGEZtype())
    {
                            if(rRF.isbusy(instruction.op1))
                            {
                                if(rROB.isready(rRF.getROB(instruction.op1)))
                                {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op1)); station[r].qj=-1;}
                                else
                                station[r].qj=rRF.getROB(instruction.op1);

                            }
                            else
                            {station[r].vj=rRF.getvalue(instruction.op1);station[r].qj=-1;}
                            station[r].qk=-1;
    }
    if(station[r].opBLEZtype())
    {
                                if(rRF.isbusy(instruction.op1))
                        {
                            if(rROB.isready(rRF.getROB(instruction.op1)))
                            {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op1)); station[r].qj=-1;}
                            else
                            station[r].qj=rRF.getROB(instruction.op1);

                        }
                        else
                        {station[r].vj=rRF.getvalue(instruction.op1);station[r].qj=-1;}
                         station[r].qk=-1;
                    }
    if(station[r].opLWtype())
    {
        if(rRF.isbusy(instruction.op1))
                        {
                            if(rROB.isready(rRF.getROB(instruction.op1)))
                            {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op1)); station[r].qj=-1;}
                            else
                            station[r].qj=rRF.getROB(instruction.op1);

                        }
                        else
                        {station[r].vj=rRF.getvalue(instruction.op1);station[r].qj=-1;}
                         station[r].qk=-1;
                         station[r].vk=instruction.immediate;
    }
    if(station[r].opSWtype())
    {
       // cout<<"\n\n**********"<<rRF.list[6].ROBid<<" "<<rRF.list[6].busy;
        //cout<<"\n\n"<<instruction.op1<<" "<<instruction.op2<<"*U*U***U**U**U*U*U\n\n";
        if(rRF.isbusy(instruction.op2))
        {
            if(rROB.isready(rRF.getROB(instruction.op2)))
            {station[r].vj=rROB.getvalue(rRF.getROB(instruction.op2)); station[r].qj=-1;}
            else
            station[r].qj=rRF.getROB(instruction.op2);

        }
        else
        {station[r].vj=rRF.getvalue(instruction.op2);station[r].qj=-1;}

        station[r].vk=instruction.immediate;
        station[r].qk=-1;

    }

}
int rStation_member::opADDtype()
{
    if(!inst.opcode.compare("ADD")||!inst.opcode.compare("SUB")||!inst.opcode.compare("SLT")||!inst.opcode.compare("SLTU")||!inst.opcode.compare("SUBU")||!inst.opcode.compare("ADDU")||!inst.opcode.compare("AND")||!inst.opcode.compare("OR")||!inst.opcode.compare("XOR")||!inst.opcode.compare("NOR"))
        return 1;
    return 0;

}
    int rStation_member::opADDItype()
    {
        if(!inst.opcode.compare("ADDI")||!inst.opcode.compare("ADDIU")||!inst.opcode.compare("SLTI"))
            return 1;
        else
            return 0;
    }
    int rStation_member::opSLLtype()
    {
        if(!inst.opcode.compare("SLL")||!inst.opcode.compare("SRA")||!inst.opcode.compare("SRL"))
            return 1;
        return 0;
    }

    int rStation_member::opBNEtype()
    {
        if(!inst.opcode.compare("BEQ")||!inst.opcode.compare("BNE"))
        {
            return 1;
        }
        return 0;
    }
    int rStation_member::opBGEZtype()
    {
        if(!inst.opcode.compare("BGEZ")||!inst.opcode.compare("BLTZ"))
            return 1;
        return 0;
    }
    int rStation_member::opBLEZtype()
    {
        if(!inst.opcode.compare("BLEZ")||!inst.opcode.compare("BGTZ"))
            return 1;
        return 0;
    }
    int rStation_member::opLWtype()
    {
        if(!inst.opcode.compare("LW"))
            return 1;
        return 0;
    }
    int rStation_member::opSWtype()
    {
        if(!inst.opcode.compare("SW"))
            return 1;
        return 0;
    }
    void RS::fprint()
    {
        cout<<"RS:\n";
                for(int i=0;i<8;i++)
                {
                    if(station[i].busy)
                    {cout<<"[";
                        station[i].inst.fprint();
                        cout<<"]";
                        if(debug)
                            cout<<"[vj:"<<station[i].vj<<" vk:"<<station[i].vk<<" qj:"<<station[i].qj<<" qk:"<<station[i].qk<<" rob:"<<station[i].robid<<" exe:"<<station[i].executed<<"]";
                         cout<<"\n";
                    }

                }
    }

    int RS::execute(int cycle,CDB &rCDB,BTB &rBTB,ROB &rROB,iQueue &rIQ)
    {
        int result;int value;
        for(int i=0;i<8;i++)
        {
            if((station[i].busy) && (station[i].qj==-1) && (station[i].qk==-1) && (station[i].cycle<cycle) && !station[i].executed )
            {
                if(debug)
                cout<<"Executing Instruction ";station[i].inst.fprint();cout<<endl;
                result=station[i].execute(rBTB,rROB,cycle,rIQ,rCDB);
                value=station[i].a;
                station[i].executed=1;

                if(flushed)
                    flush(i);
                if(!station[i].inst.isjump)
                {  if(!station[i].inst.opcode.compare("LW"))
                    {

                    rCDB.add(cycle,result,value,station[i].robid,station[i].inst.opcode,1,i);
                    }
                    else
                    rCDB.add(cycle,result,value,station[i].robid,station[i].inst.opcode,i);
                }
            }
        }
    }

    int rStation_member::execute(BTB &rBTB, ROB &rROB,int rcycle,iQueue &rIQ,CDB &rCDB)
    {
        int result;
        if(!inst.opcode.compare("ADD")||!inst.opcode.compare("ADDI"))
        {
            result=vj+vk;
        }
        if(!inst.opcode.compare("ADDIU")||!inst.opcode.compare("ADDU"))
        {
            result=(unsigned int)vj+(unsigned int)vk;
        }
        if(!inst.opcode.compare("SUB"))
        {
            result=vj-vk;
        }
        if(!inst.opcode.compare("SUBU"))
        {
            result=(unsigned int)vj-(unsigned int)vk;
        }
        if(!inst.opcode.compare("AND"))
        {
            result=vj&&vk;
        }
        if(!inst.opcode.compare("LW"))
        {
            result=0;
            a=vj+vk;
        }
        if(!inst.opcode.compare("SW"))
        {
            result=vj+vk;
            a=inst.op1;
        }
        if(!inst.opcode.compare("NOR"))
        {
            result=!(vj||vk);
        }
        if(!inst.opcode.compare("OR"))
        {
            result=vj||vk;
        }
        if(!inst.opcode.compare("SLL"))
        {
            result=vj<<vk;
        }
        if(!inst.opcode.compare("SLT"))
        {
            result=vj<vk;
        }
        if(!inst.opcode.compare("SLTI"))
        {
            result=vj<vk;
        }
        if(!inst.opcode.compare("SLTI"))
        {
            result=vj<(unsigned int)vk;
        }
        if(!inst.opcode.compare("XOR"))
        {
            result=vj^vk;
        }
        if(!inst.opcode.compare("BEQ"))
        {
            result=(vj==vk);
        }
        if(!inst.opcode.compare("BGEZ"))
        {
            result=vj>0;
        }
        if(!inst.opcode.compare("J"))
        {
           
            result=1;
        }
        if(!inst.opcode.compare("BGTZ"))
        {
            result=vj>0;
        }
        if(!inst.opcode.compare("BLEZ"))
        {
            result=(vj<=0);
        }
        if(!inst.opcode.compare("BLTZ"))
        {
            result=(vj<0);
        }
        if(!inst.opcode.compare("BNE"))
        {
            result=(vj!=vk);
        }
        if(!inst.opcode.compare("SRA"))
        {
            result=(vj>>vk);
        }
        if(!inst.opcode.compare("SRL"))
        {
            result=((unsigned)vj>>vk);
        }
        if(inst.isjump)
        {
            if((result==1 && rBTB.taken(inst.address))||( result==0 && rBTB.nottaken(inst.address)))
            {
                rROB.ROBlist[robid].state=COMMIT;
                rROB.ROBlist[robid].updatecycle=rcycle;
               rsremovelist.push(id);
                //cout<<"\nNOTE THIS :::::::::"<<id<<" was set\n";
                return result;
            }
            else
            {
                
                //cout<<"did it reach here ?************************";
                rROB.ROBlist[robid].state=COMMIT;
                rROB.ROBlist[robid].updatecycle=rcycle;
                rsremovelist.push(id);

                if(result==1)
                rBTB.settaken(inst.address);
                if(result==0)
                rBTB.setnottaken(inst.address);

                //rROB.flush(inst.issuecycle);
                //rIQ.flush();
                //rCDB.flush();
                flushed=1;
                flushedby=inst;
                //cout<<"\nPROGRAM counter was *******"<<PC;
                //rBTB.fprint();
                //PC=rBTB.getaddr(inst.address);
                //cout<<"\nPROGRAM counter is ******* "<<PC;
            }
                //the end of the world;
        
        }
        return result;
    }
    void RS::update(CDBmember data, int robid, int cycle)
    {
        if(!data.optype.compare("SW"))
            return;
        for(int i=0;i<8;i++)
        {
            if(station[i].busy)
            {
                if(station[i].cycle<cycle)
                {
                    if(station[i].qj==robid)
                    {
                        station[i].vj=data.result;
                        station[i].qj=-1;
                    }
                    else if(station[i].qk==robid)
                    {
                        station[i].vk=data.result;
                        station[i].qk=-1;
                    }
                }
            }
        }
    }
    void RS::flush(int i)
    {
        int cyc=station[i].inst.issuecycle;
        for(int i=0;i<8;i++)
        {
            if(station[i].cycle>cyc)
            {
                if(debug)
                {
                    cout<<"Flushed out ";station[i].inst.fprint();
                    cout<<endl;
                }

                station[i].executed=0;
                station[i].busy=0;
            }
        }
    }